/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.controller;

import com.alibaba.excel.event.SyncReadListener;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import org.elsfs.cloud.common.core.vo.R;
import org.elsfs.cloud.common.excel.controller.ExcelUploadController;
import org.elsfs.cloud.common.mybatis.co.EditStateCo;
import org.elsfs.cloud.common.mybatis.repository.IElsfsRepository;
import org.elsfs.cloud.common.mybatis.utils.DateQuery;
import org.elsfs.cloud.common.util.exception.NotSupportedException;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

/**
 * 默认的增删改查接口
 *
 * @author zeng
 * @param <E> 实体类型
 * @param <Q> 实体类型
 * @param <Add> 实体类型
 * @param <Edit> 实体类型
 * @param <ER> 继承自 IElsfsRepository 的实体类型
 * @param <ID> id 类型
 */
public class RepositoryCrudController<
        E, Q extends E, Add extends E, Edit extends E, ER extends IElsfsRepository<E, ID>, ID>
    implements ExcelUploadController<E> {
  @SuppressWarnings("all")
  @Autowired
  protected ER repository;

  protected ControllerConfig getConfig() {
    return new ControllerConfig()
        .list(false)
        .page(true)
        .getById(true)
        .add(true)
        .edit(true)
        .editState(true)
        .removeById(true)
        .removeBatch(true)
        .exportExcel(true)
        .exportExcel(true)
        .logicPage(false);
  }

  @GetMapping("/page")
  @Operation(summary = "分页查询", description = "分页查询")
  public R<Page<E>> page(@ParameterObject Q entity, @ParameterObject DateQuery<E> qry) {
    if (!getConfig().page()) {
      return R.error("不支持分页查询");
    }
    return R.success(repository.page(qry.toPage(), qry.query(entity)));
  }

  @Operation(summary = "查询列表", description = "查询列表")
  @GetMapping("/list")
  public R<List<E>> list(E entity) {
    if (!getConfig().list()) {
      return R.error("不支持查询列表");
    }
    return R.success(repository.list(Wrappers.query(entity)));
  }

  /**
   * 通过id查询数据源表
   *
   * @param id id
   * @return R
   */
  @GetMapping("/getById/{id}")
  @Operation(summary = "通过id查询", description = "通过id查询")
  public R<? extends E> getById(@PathVariable("id") String id) {
    if (!getConfig().getById()) {
      return R.error("不支持通过 id 获取数据");
    }
    return R.success(repository.getById(id));
  }

  @PutMapping("/editState")
  @Operation(summary = "修改状态", description = "修改状态")
  public R<Boolean> editState(@RequestBody @Validated EditStateCo co) {
    if (!getConfig().editState()) {
      return R.error("不支持修改状态");
    }
    return R.success(repository.editState(co));
  }

  @PostMapping("/add")
  @Operation(summary = "添加接口", description = "添加接口")
  public R<Boolean> add(@RequestBody @Validated Add co) {
    if (!getConfig().add()) {
      return R.error("不支持添加数据");
    }
    check(co, false);
    return R.success(repository.save(co), "添加成功");
  }

  @PutMapping("/edit")
  @Operation(summary = "修改接口", description = "修改接口")
  public R<Boolean> edit(@RequestBody @Validated Edit co) {
    if (!getConfig().edit()) {
      return R.error("不支持修改数据");
    }
    check(co, true);
    return R.success(repository.updateById(co), "修改成功");
  }

  @DeleteMapping("/removeById/{id}")
  @Operation(summary = "根据id删除接口", description = "根据id删除接口")
  public R<Boolean> removeById(@PathVariable("id") String id) {
    if (!getConfig().removeById()) {
      return R.error("不支持删除数据");
    }
    return R.success(repository.removeById(id), "删除成功");
  }

  @DeleteMapping("/removeBatchByIds")
  @Operation(summary = "批量删除接口", description = "批量删除接口")
  public R<Boolean> removeBatchByIds(@RequestBody List<String> ids) {
    if (!getConfig().removeBatch()) {
      return R.error("不支持删除数据");
    }
    return R.success(repository.removeByIds(ids), "删除成功");
  }

  @SuppressWarnings("unchecked")
  @GetMapping("/exportXls")
  @Operation(summary = "导出xls", description = "导出xls")
  public void exportXls(
      @ParameterObject E entity,
      @ParameterObject DateQuery<E> dateQuery,
      HttpServletResponse response) {
    if (!getConfig().exportExcel()) {
      throw new NotSupportedException("不支持导出数据");
    }
    List<E> list = repository.list(dateQuery.query(entity));
    try {
      exportXls(list, response, "cs", (Class<E>) entity.getClass());
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  @PostMapping("/importExcel")
  public R<?> importExcel(@RequestParam("file") MultipartFile multipartFile) throws Exception {
    if (!getConfig().importExcel()) {
      R.error("不支持导入数据");
    }
    List<E> list = importExcel(multipartFile, repository.getEntityClass(), new SyncReadListener());
    return importExcelHandle(list);
  }

  protected R<?> importExcelHandle(List<E> list) {
    repository.saveBatch(list);
    return R.success("导入成功");
  }

  /**
   * 新增 修改 校验
   *
   * @param entity 实体
   * @param isUpdate 是否更新
   */
  protected void check(E entity, boolean isUpdate) {}
}
